package sjcc

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

/* package local var, compiler tools base dir */
var (
	bpath  string
	binc   string
	blib   string
	bclang string
	blink  string
)

func init() {
	var err error

	/* compiler tools base dir */
	self, _ := exec.LookPath(os.Args[0])
	bpath, _ = filepath.Abs(self)
	bpath = filepath.Dir(bpath)

	/**/
	bclang, err = exec.LookPath("clang")
	if err != nil {
		fmt.Println("err : need install 'clang' package")
		os.Exit(1000)
	}

	blink, err = exec.LookPath("ld.lld")
	if err != nil {
		fmt.Println("err : need install 'lld' package")
		os.Exit(1001)
	}

	binc = filepath.Join(bpath, "../include")
	blib = filepath.Join(bpath, "../lib")

	/**/
	// fmt.Println(">> clang:", bclang)
	// fmt.Println(">> inc:", binc)
	// fmt.Println(">> lib:", blib)
}

func maddArgs(exx *exec.Cmd, arg string) {
	args := strings.Split(arg, " ")
	exx.Args = append(exx.Args, args...)
}

type Comp struct {
	cpu string
	src string
	tar string

	macs []string
	incs []string
	args []string
}

func NewComp(cpu, src, tar string) (*Comp, error) {
	return &Comp{cpu: cpu, src: src, tar: tar, args: []string{}}, nil
}

func (cmd *Comp) AddMacs(macs []string) {
	cmd.macs = append(cmd.macs, macs...)
}

func (cmd *Comp) AddIncs(incs []string) {
	cmd.incs = append(cmd.incs, incs...)
}

func (cmd *Comp) AddArgs(args []string) {
	cmd.args = append(cmd.args, args...)
}

func (cmd *Comp) CreateExec() (*exec.Cmd, error) {

	/**/
	if len(cmd.tar) <= 0 {
		return nil, errors.New("not set cc target..")
	}

	if len(cmd.src) <= 0 {
		return nil, errors.New("not set cc source..")
	}

	/**/
	exx := exec.Command(bclang, "-cc1", "-x", "c", "-std=gnu99")
	maddArgs(exx, "-fgnuc-version=4.2.1")
	maddArgs(exx, "-disable-free -disable-llvm-verifier -mrelocation-model static -mconstructor-aliases -fno-caret-diagnostics -fno-diagnostics-fixit-info")

	/**/
	if cmd.cpu == "cortex-A5" {
		maddArgs(exx, "-triple thumbv7a-none-eabi -target-cpu cortex-a5 -target-feature -hwdiv -target-feature -hwdiv-arm -target-feature -virtualization")
		maddArgs(exx, "-target-feature -strict-align -target-feature +soft-float -target-feature +soft-float-abi -target-feature -vfp2 -target-feature -neon")
		maddArgs(exx, "-msoft-float -target-abi aapcs -mfloat-abi soft")
		maddArgs(exx, "-D__SIZEOF_WCHAR_T=4 -D__ARM_ARCH_7A__ -D__CROSSWORKS_ARM")

	} else if cmd.cpu == "cortex-M4" {
		maddArgs(exx, "-triple thumbv6m-none-eabi -target-cpu cortex-m0plus -target-feature +strict-align -target-feature +soft-float")
		maddArgs(exx, "-target-feature +soft-float-abi -msoft-float -target-abi aapcs -mfloat-abi soft")
		maddArgs(exx, "-D__SIZEOF_WCHAR_T=4 -D__ARM_ARCH_6M__ -D__CROSSWORKS_ARM")

	} else if cmd.cpu == "cortex-M0" {
		maddArgs(exx, "-triple thumbv6m-none-eabi -target-cpu cortex-m0plus -target-feature +strict-align -target-feature +soft-float")
		maddArgs(exx, "-target-feature +soft-float-abi -msoft-float -target-abi aapcs -mfloat-abi soft")
		maddArgs(exx, "-D__SIZEOF_WCHAR_T=4 -D__ARM_ARCH_6M__ -D__CROSSWORKS_ARM")

	} else {
		return nil, errors.New("not know the cpu : " + cmd.cpu)
	}

	maddArgs(exx, "-fno-diagnostics-fixit-info -fno-dwarf-directory-asm -fno-builtin -fmath-errno")
	maddArgs(exx, "-nostdsysteminc -nobuiltininc -isystem "+binc)

	for _, v := range cmd.macs {
		exx.Args = append(exx.Args, "-D", v)
	}

	for _, v := range cmd.incs {
		exx.Args = append(exx.Args, "-I", v)
	}

	for _, v := range cmd.args {
		maddArgs(exx, v)
	}

	exx.Args = append(exx.Args, cmd.src, "-emit-obj", "-o", cmd.tar)
	return exx, nil
}

type Asmb struct {
	cpu  string
	src  string
	mid  string
	tar  string
	macs []string
	incs []string
	args []string
}

func NewAsmb(cpu, src, mid, tar string) (*Asmb, error) {
	return &Asmb{cpu: cpu, src: src, mid: mid, tar: tar, args: []string{}}, nil
}

func (cmd *Asmb) AddMacs(macs []string) {
	cmd.macs = append(cmd.macs, macs...)
}

func (cmd *Asmb) AddIncs(incs []string) {
	cmd.incs = append(cmd.incs, incs...)
}

func (cmd *Asmb) AddArgs(args []string) {
	cmd.args = append(cmd.args, args...)
}

func (cmd *Asmb) CreatePreproc() (*exec.Cmd, error) {

	/**/
	if len(cmd.tar) <= 0 {
		return nil, errors.New("not set target..")
	}

	if len(cmd.src) <= 0 {
		return nil, errors.New("not set source..")
	}

	/**/
	exx := exec.Command(bclang, "-cc1", "-x", "assembler-with-cpp", "-E", "-P")
	maddArgs(exx, "-disable-free -disable-llvm-verifier -mrelocation-model static -mconstructor-aliases -fno-caret-diagnostics -fno-diagnostics-fixit-info")

	if cmd.cpu == "cortex-A5" {
		maddArgs(exx, "-triple thumbv7a-none-eabi -target-cpu cortex-a5 -target-feature -hwdiv -target-feature -hwdiv-arm -target-feature -virtualization")
		maddArgs(exx, "-target-feature -strict-align -target-feature +soft-float -target-feature +soft-float-abi -target-feature -vfp2 -target-feature -neon")
		maddArgs(exx, "-msoft-float -target-abi aapcs -mfloat-abi soft")
	} else {
		return nil, errors.New("not know the cpu : " + cmd.cpu)
	}

	maddArgs(exx, "-nostdsysteminc -nobuiltininc -isystem "+binc)

	for _, v := range cmd.macs {
		exx.Args = append(exx.Args, "-D", v)
	}

	for _, v := range cmd.incs {
		exx.Args = append(exx.Args, "-I", v)
	}

	for _, v := range cmd.args {
		maddArgs(exx, v)
	}

	exx.Args = append(exx.Args, cmd.args[:]...)
	exx.Args = append(exx.Args, cmd.src, "-o", cmd.mid)
	return exx, nil
}

func (cmd *Asmb) CreateAssemble() (*exec.Cmd, error) {
	/**/
	if len(cmd.tar) <= 0 {
		return nil, errors.New("not set target..")
	}

	if len(cmd.src) <= 0 {
		return nil, errors.New("not set source..")
	}

	/**/
	exx := exec.Command(bclang, "-cc1as", "-filetype", "obj")
	maddArgs(exx, "-mrelocation-model static")

	if cmd.cpu == "cortex-A5" {
		maddArgs(exx, "-triple thumbv7a-none-eabi -target-cpu cortex-a5 -target-feature -hwdiv -target-feature -hwdiv-arm -target-feature -virtualization")
		maddArgs(exx, "-target-feature -strict-align -target-feature -vfp2 -target-feature -neon")
		maddArgs(exx, "-mllvm -arm-add-build-attributes")
	} else {
		return nil, errors.New("not know the cpu : " + cmd.cpu)
	}

	exx.Args = append(exx.Args, cmd.args[:]...)
	exx.Args = append(exx.Args, cmd.mid, "-o", cmd.tar)
	return exx, nil

}
